from classfile.constant import ConstantFieldRef, ConstantMethodRef
from heap.class_klass import Klass


class SystemRef:
    def __init__(self, className, cp):  # cp : KlassConstantPool
        self.klass = None
        self.className = className
        self.cp = cp

    def resolveClass(self) -> Klass:
        if self.klass is None:
            return self.resolveClassInternal()
        return self.klass

    def resolveClassInternal(self):
        c = self.cp.klass.classLoader.loadClass(self.className)
        if not c.isAccessibleTo(self.cp.klass):
            raise Exception("Class not accessible")
        self.klass = c
        return c


class ClassRef(SystemRef):
    def __init__(self, className, cp):
        super().__init__(className, cp)


class MemberRef(SystemRef):
    def __init__(self, className, cp, ):
        super().__init__(className, cp)
        self.name = None
        self.descriptor = None

    def copyField(self, ref: ConstantFieldRef):
        self.className = ref.klass.className.value
        self.name = ref.getFieldName()
        self.descriptor = ref.getFieldDescriptor()

    def copyMethod(self, ref: ConstantMethodRef):
        self.className = ref.klass.className.value
        self.name = ref.getMethodName()
        self.descriptor = ref.getDescriptor()


class FieldRef(MemberRef):
    def __init__(self, className, cp):
        super().__init__(className, cp)
        self.field = None

    def resolveField(self):
        if self.field is not None:
            return self.field
        klass = self.cp.getKlass()
        fieldKlass = self.resolveClass()
        newField= klass.findField(self.name, self.descriptor, False)
        if newField is None:
            raise Exception("Field not found")
        if not newField.isAccessibleTo(klass):
            raise Exception("Field not accessible")
        self.field = newField
        return self.field


class MethodRef(MemberRef):
    def __init__(self, className, cp):
        super().__init__(className, cp)
        self.method = None

    def resolveMethod(self):
        # 调用类
        d = self.cp.klass
        # 所属类
        c = self.resolveClass()
        if not c.isInterface():
            raise Exception("Method not found")
        method = c.findMethod(self.name, self.descriptor)
        if method is None:
            raise Exception("Method not found")
        if not method.isAccessibleTo(d):
            raise Exception("Method not accessible")
        self.method = method
        return self.method


class InterfaceMethodRef(MethodRef):
    def __init__(self, className, cp):
        super().__init__(className, cp)

    def resolveInterfaceMethod(self):
        d = self.cp.klass
        c = self.resolveClass()
        if not c.isInterface():
            raise Exception("Method not found")
        method = c.findInterfaceMethod(self.name, self.descriptor)
        if method is None:
            raise Exception("Method not found")
        if not method.isAccessibleTo(d):
            raise Exception("Method not accessible")
        self.method = method
        return self.method
